/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-07-09     82752       the first version
 */

#define DBG_TAG              "lora_net_manager"
#define DBG_LVL              DBG_LOG

#include <rtdbg.h>
#include "lora_net_manager.h"

struct lora_net_manager g_lora_net_manager = {0};
extern void get_node_data_callback(uint16_t addr, uint8_t *data, uint8_t len);

static int lora_node_list_init(rt_list_t **head)
{
    if (*head == NULL) {
        *head = rt_malloc(sizeof(rt_list_t));
        if (*head) {
            rt_list_init(*head);
            return 0;
        } else {
            return -1;
        }
    }
    return -1;
}

static struct lora_node *find_lora_node(struct rt_list_node *head, uint16_t addr)
{
    struct rt_list_node *pos;
    struct lora_node *ln;
    struct rt_mutex *mutex = &g_lora_net_manager.lora_list_mutex;


    if (head == NULL) {
        return NULL;
    }

    rt_mutex_take(mutex, RT_WAITING_FOREVER);

    rt_list_for_each(pos, head)
    {
        ln = rt_list_entry(pos, lora_node_t, entry);
        if (ln->addr == addr) {

            rt_mutex_release(mutex);
            return ln;
        }
    }

    rt_mutex_release(mutex);

    return NULL;
}

static int add_lora_node(struct rt_list_node *head, struct lora_node *ln)
{
    int ret = 0;
    struct rt_mutex *mutex = &g_lora_net_manager.lora_list_mutex;

    if (ln == NULL || head == NULL) {
        return -1;
    }

    ret = rt_mutex_take(mutex, RT_WAITING_FOREVER);
    if (ret != RT_EOK) {
        rt_kprintf("cur lora list mutex take failed\n");
        return ret;
    }

    rt_list_insert_before(head, &ln->entry);

    rt_mutex_release(mutex);

    return ret;
}

static int delete_lora_node(struct rt_list_node *head, uint16_t addr)
{
    int ret = 0;

    struct rt_mutex *mutex = &g_lora_net_manager.lora_list_mutex;

    struct rt_list_node *pos;
    struct rt_list_node *tmp;
    struct lora_node *ln;

    rt_mutex_take(mutex, RT_WAITING_FOREVER);

    rt_list_for_each_safe(pos, tmp, head)
    {
        ln = rt_list_entry(pos, lora_node_t, entry);
        if (ln->addr == addr) {
            rt_list_remove(pos);
            rt_free(ln);

            ret = rt_mutex_release(mutex);
            return 0;
        }
    }

    rt_mutex_release(mutex);

    ret = -1;
    return ret;
}

static int get_lora_node_addr_list(struct rt_list_node *head, uint16_t *addr_array)
{
    uint8_t i = 0;
    struct lora_node *ln;
    struct rt_list_node *pos;
    struct rt_mutex *mutex = &g_lora_net_manager.lora_list_mutex;

    rt_mutex_take(mutex, 1000);

    rt_list_for_each(pos, head)
    {
        ln = rt_list_entry(pos, lora_node_t, entry);
        addr_array[i] = ln->addr;

        i++;
    }

    rt_mutex_release(mutex);

    return i;
}

static struct lora_node_list_opt ln_list_opt = {
    .lora_node_list_init     = lora_node_list_init,
    .add_lora_node           = add_lora_node,
    .delete_lora_node        = delete_lora_node,
    .get_lora_node_addr_list = get_lora_node_addr_list,
    .get_lora_node_from_addr = find_lora_node,
};

void update_lora_net_online_list(struct rt_work *work, void *work_data)
{
    uint8_t num = 0;
    uint16_t online_addr[MAX_LORA_NODE] = {0};

    struct lora_node *ln = NULL;
    struct rt_list_node *tmp = NULL;
    struct rt_list_node *pos = NULL;

    struct lora_net_manager *p_ln_manager = (struct lora_net_manager *)work_data;
    rt_list_t *head = p_ln_manager->lora_node_list_head;
    struct rt_mutex *mutex = &p_ln_manager->lora_list_mutex;

    if(head == NULL) {
        return;
    }

    rt_mutex_take(mutex, 1000);
    rt_list_for_each_safe(pos, tmp, head)
    {
        ln = rt_list_entry(pos, lora_node_t, entry);
        if (ln->status != 0) {
            ln->status = 0;
            ln->unlink_count = 0;
        } else {
            ln->unlink_count++;
            if (ln->unlink_count >= UNLINK_COUNT) {
                rt_list_remove(&ln->entry);
                rt_free(ln);
            }
        }
    }
    rt_mutex_release(mutex);

    num = p_ln_manager->opt->get_lora_node_addr_list(head, (uint16_t *)online_addr);
    if (num < 0) {
        LOG_E("get lora node list error %d\n", num);
    } else {
        LOG_I("the lora node list len is %d\n", (int)rt_list_len(head));
    }
}

int lora_net_send_msg(struct lora_net_manager *p_ln_manager,
                      uint16_t dest_addr, uint8_t dest_channel,
                      uint8_t *msg, uint8_t size)
{
    rt_device_t lora_dev = p_ln_manager->lora_dev;
    uint8_t buf[LORA_MAX_REV + 3] = {0};

    buf[0] = dest_addr >> 8;
    buf[1] = dest_addr & 0xFF;
    buf[2] = dest_channel;
    rt_memcpy(&buf[3], msg, size);

    return rt_device_write(lora_dev, 0, buf, size + 3);
}

int lora_net_send_cmd(struct lora_net_manager *p_ln_manager,
                      uint16_t dest_addr, uint8_t dest_channel,
                      uint16_t cmd)
{
    uint8_t msg[2] = {(cmd >> 8), (cmd & 0xFF)};
    return lora_net_send_msg(p_ln_manager,
                             dest_addr,
                             dest_channel,
                             msg,
                             2);
}


static int add_new_lora_node(struct lora_net_manager *p_ln_manager, uint16_t addr) {
    struct lora_node *ln = NULL;
    rt_list_t *head = p_ln_manager->lora_node_list_head;
    ln = (struct lora_node *)rt_malloc(sizeof(struct lora_node));

    if (ln != NULL) {
        rt_memset(ln, 0, sizeof(struct lora_node));
        ln->addr = addr;
        p_ln_manager->opt->add_lora_node(head, ln);
        LOG_I("one new lora node added with addr 0x%x, list len is %d\n", addr, (int)rt_list_len(head));
    } else {
        LOG_E("malloc for lora node failed\n");
        return -1;
    }
    return 0;
}

int is_lora_node_addr_in_net(uint16_t addr)
{

    struct lora_node *ln = NULL;
    struct lora_net_manager *p_ln_manager = &g_lora_net_manager;
    rt_list_t *head = p_ln_manager->lora_node_list_head;

    ln = p_ln_manager->opt->get_lora_node_from_addr(head, addr);
    if (ln != NULL) {
        return 1;
    } else {
        return 0;
    }
}

ALIGN(RT_ALIGN_SIZE);
static char lnm_thread_stack[2048];
void lora_net_communication_process(void *param)
{
    int ret = 0;
    uint8_t  send_msg[16] = {0};
    uint8_t  lora_buf[LORA_MAX_REV] = {0};
    uint8_t  len = 0;
    uint8_t  *msg_start = NULL;
    uint8_t  lora_node_channel = 0;
    uint16_t lora_node_addr = 0;
    uint16_t cmd = 0;

    struct lora_node *ln = NULL;
    struct lora_net_manager *p_ln_manager = (struct lora_net_manager *)param;

    //rt_device_t lora_dev = p_ln_manager->lora_dev;
    rt_device_t lora_dev = rt_device_find("lora_as32");
    rt_list_t *head = p_ln_manager->lora_node_list_head;

    while (1) {
        ret = rt_device_read(lora_dev, 0, lora_buf, LORA_MAX_REV);
        if (!ret) {
            len = lora_buf[LEN_POS_IN_LORA_NET_PACK];
            lora_node_channel = lora_buf[SRC_CHAN_POS_IN_LORA_NET_PACK];
            lora_node_addr = ((lora_buf[SRC_ADDRH_POS_IN_LORA_NET_PACK] << 8) |
                              lora_buf[SRC_ADDRL_POS_IN_LORA_NET_PACK]);
            cmd = ((lora_buf[CMDH_POS_IN_LORA_NET_PACK] << 8) |
                  lora_buf[CMDL_POS_IN_LORA_NET_PACK]);
            msg_start = &lora_buf[DATA_START_POS_IN_LORA_NET_PACK];

            if (cmd != LORA_NET_CONNECT) {
                if(!is_lora_node_addr_in_net(lora_node_addr)) {
                    continue;
                }
            }

            LOG_D("recieve msg from 0x%x, cmd: %d, len: %d", lora_node_addr, cmd, len);

            switch (cmd) {

            case LORA_NET_CMD_REPORT_NODE_DATA:
                if (p_ln_manager->get_node_data_callback != NULL) {
                    get_node_data_callback(lora_node_addr, msg_start, len - 5);
                    //p_ln_manager->get_node_data_callback(lora_node_addr, msg_start, len - 5);
                } else {
                    LOG_E("node data callback is NULL");
                }
                break;

            case LORA_NET_CONNECT:
                if (lora_node_addr != 0) {

                    if (rt_list_len(head) > MAX_LORA_NODE) {
                        LOG_E("there is no space for new lora node\n");
                        break;
                    }

                    ln = p_ln_manager->opt->get_lora_node_from_addr(head, lora_node_addr);
                    if (ln != NULL) {
                        lora_net_send_cmd(p_ln_manager,
                                          lora_node_addr,
                                          lora_node_channel,
                                          LORA_NET_RECONNECT);
                    } else {
                        add_new_lora_node(p_ln_manager, lora_node_addr);

                        send_msg[0] = lora_node_addr >> 8;
                        send_msg[1] = lora_node_addr & 0xFF;
                        send_msg[2] = lora_node_channel;
                        send_msg[3] = LORA_NET_CONNECT_OK_ACK >> 8;
                        send_msg[4] = LORA_NET_CONNECT_OK_ACK & 0xFF;
                        rt_device_write(lora_dev, 0, send_msg, 5);
                    }
                }

                break;

            case LORA_NET_HEARTBEAT:
                ln = p_ln_manager->opt->get_lora_node_from_addr(head, lora_node_addr);
                if(NULL != ln) {
                    LOG_D("recieve heartbeat from %x\n", lora_node_addr);
                    ln->status = 1;
                }
                break;

            default:
                break;
            }

        }
    }
}

static void heartbeat_timer_process(void* parameter)
{
    struct lora_net_manager *p_ln_manager = (struct lora_net_manager *)parameter;
    LOG_D("\n--update the lora net--\n");
    rt_work_submit(&p_ln_manager->update_lora_net_work, 2);
}

int lora_net_manager_init(rt_device_t lora_dev, get_node_data_callback_t callback)
{
    int ret = 0;
    struct lora_net_manager *p_ln_manager = &g_lora_net_manager;
    p_ln_manager->opt = &ln_list_opt;

    ret = p_ln_manager->opt->lora_node_list_init(&p_ln_manager->lora_node_list_head);
    if (ret != 0) {
        LOG_E("lora node list init failed\n");
        return -1;
    }

    if (0 != rt_mutex_init (&p_ln_manager->lora_list_mutex,
                                "lora_list_mutext", RT_IPC_FLAG_PRIO)) {
        LOG_E("lora net mutex init failed\n");
        return -1;                 //error handle
    }

    rt_work_init(&p_ln_manager->update_lora_net_work,
                 update_lora_net_online_list,
                 (void *)p_ln_manager);

    rt_timer_init(&p_ln_manager->heartbeat_timer,
                  "heartbeat_timer",
                  heartbeat_timer_process, /* ��ʱʱ�ص��Ĵ������� */
                  (void *)p_ln_manager, /* ��ʱ��������ڲ��� */
                  8000, /* ��ʱ���ȣ��� OS Tick Ϊ��λ���� 10 �� OS Tick */
                  RT_TIMER_FLAG_PERIODIC); /* �����Զ�ʱ�� */

    p_ln_manager->lora_dev = lora_dev;
    p_ln_manager->lora_net_update_flag = 0;
    p_ln_manager->get_node_data_callback = callback;

    rt_timer_start(&p_ln_manager->heartbeat_timer);

    rt_thread_init(&p_ln_manager->lnm_thread,
                   "lora_net_manage_thread",
                   lora_net_communication_process,
                   (void *)p_ln_manager,
                   &lnm_thread_stack[0],
                   sizeof(lnm_thread_stack),
                   10, 5);
    rt_thread_startup(&p_ln_manager->lnm_thread);

    return 0;
}



